【Tips寄り】S3に配置したJavaのソースコードをCodePipelineでECSにデプロイしてみた

【Tips寄り】S3に配置したJavaのソースコードをCodePipelineでECSにデプロイしてみた

Clock Icon2025.01.07

こんにちはこーへいです!

今回は以下の記事で作成したサンプルのJavaアプリケーションをCodePipelineでECS上にデプロイするまでの流れを紹介します。

https://dev.classmethod.jp/articles/java-app-container-local-setup/

細かい設定の参考は記事内で別ブログを紹介しつつ、今回はTipsや全体の流れの紹介メインで解説していきます。

前提

  • VPC周りのリソースは作成済み
  • ある程度ECSやCodeシリーズを触ったことがあり、大まかな設定は把握済み

手動で起動する

CodePipelineでデプロイを自動化する前に、まずは手動でJavaアプリをデプロイします。

ECRの準備

貼り付けた画像_2025_01_07_14_29

ECRにリポジトリ「java/test(アプリケーション名/環境名)」を作成し、ローカルにあるコンテナイメージをプッシュします。

貼り付けた画像_2025_01_07_14_32

ECRにログインやプッシュコマンドについては、「プッシュコマンドを表示」から確認することが可能です、親切ですね。

ECSにて起動

ECSにてタスク定義を作成し、サービスを作成しJavaアプリケーションを起動してみます。

今回作成したタスク定義は以下となります(一部マスクしています)。

また、自分のローカル端末はM2 MacでデフォルトでコンテナイメージをビルドしてしまうとアーキテクチャがARMになってしまいます、そのためECSや後述するCodeBuildの実行環境もARMベースを採用しています。

タスク定義
{
    "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task-definition/java:1",
    "containerDefinitions": [
        {
            "name": "java",
            "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/java/test:[イメージタグ]",
            "cpu": 0,
            "portMappings": [
                {
                    "name": "java-8081-tcp",
                    "containerPort": 8081,
                    "hostPort": 8081,
                    "protocol": "tcp",
                    "appProtocol": "http"
                }
            ],
            "essential": true,
            "environment": [],
            "environmentFiles": [],
            "mountPoints": [],
            "volumesFrom": [],
            "ulimits": [],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/java",
                    "mode": "non-blocking",
                    "awslogs-create-group": "true",
                    "max-buffer-size": "25m",
                    "awslogs-region": "ap-northeast-1",
                    "awslogs-stream-prefix": "ecs"
                },
                "secretOptions": []
            },
            "systemControls": []
        }
    ],
    "family": "java",
    "taskRoleArn": "arn:aws:iam::[AWSアカウントID]:role/test-ecs-role",
    "executionRoleArn": "arn:aws:iam::[AWSアカウントID]:role/ecsTaskExecutionRole",
    "networkMode": "awsvpc",
    "revision": 1,
    "volumes": [],
    "status": "ACTIVE",
    "requiresAttributes": [
        {
            "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
        },
        {
            "name": "ecs.capability.execution-role-awslogs"
        },
        {
            "name": "com.amazonaws.ecs.capability.ecr-auth"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.28"
        },
        {
            "name": "com.amazonaws.ecs.capability.task-iam-role"
        },
        {
            "name": "ecs.capability.execution-role-ecr-pull"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
        },
        {
            "name": "ecs.capability.task-eni"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29"
        }
    ],
    "placementConstraints": [],
    "compatibilities": [
        "EC2",
        "FARGATE"
    ],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": "256",
    "memory": "512",
    "runtimePlatform": {
        "cpuArchitecture": "ARM64",
        "operatingSystemFamily": "LINUX"
    },
    "registeredAt": "2025-01-07T01:02:28.954Z",
    "registeredBy": "arn:aws:sts::[AWSアカウントID]:assumed-role/AWSCodePipelineServiceRole-ap-northeast-1-java-pipeline-2/1736211748606",
    "enableFaultInjection": false,
    "tags": []
}

貼り付けた画像_2025_01_07_17_26

ECSサービスでタスクを起動し、アクセスすると無事にHelloWorldが表示されました。

その他参考

ECRの構築方法
https://qiita.com/tora_oba/items/a2195c239afc4120ef54

ECSの構築方法
https://dev.classmethod.jp/articles/ecs-fargate-entry-new-console/

アーキテクチャの違いでエラーが出た時に確認する記事
https://entreprogrammer.jp/exec-format-error-m1-mac/

CodePipelineでCICDを構築

手動では問題なくデプロイできたので、ソースコードをS3に配置することでコンテナイメージのビルドからECSでのデプロイまでを自動化していきます。

S3の準備

通常ソースリポジトリとしてはGitHub等の採用が一般的ですが、今回はS3で試してみます。

ステップ 1: アプリケーションの S3 バケットを作成するを参考に、S3バケットを作成します。

注意点はバージョニングの有効化を忘れないことくらいだと思います。

資材の準備

https://dev.classmethod.jp/articles/java-app-container-local-setup/

こちらの記事で用意したJavaのサンプルソースコードにCodeBuildで使用するbuildspec.ymlを追加し、まとめてzip化します。

buildspec.ymlの準備

buildspec.yml
version: 0.2

phases:
  pre_build:
    commands:
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com
      - IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION
  build:
    commands:
      - docker build -t $IMAGE_REPO_NAME/$ENV:$IMAGE_TAG .
      - docker tag $IMAGE_REPO_NAME/$ENV:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME/$ENV:$IMAGE_TAG
  post_build:
    commands:
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME/$ENV:$IMAGE_TAG # ECRへプッシュ
      - printf '[{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME/$ENV:$IMAGE_TAG  > imagedefinitions.json

artifacts:
  files:
    - imagedefinitions.json

2-2-2. buildspec.ymlの作成を参考にさせていただきました。buildspec.ymlの書き方は公式ドキュメントもご参照ください。

ビルドファイルの流れとしてはECRへのログイン、イメージの作成とプッシュ、アーティファクトの作成です。

簡易化のため今回はDocker Hubへのログインを省略していますが、ベースイメージにてDocker Hubに格納されているイメージを採用している場合はIPガチャ回避のため導入してください。以下の記事が参考になります。

https://dev.classmethod.jp/articles/codebuild-has-to-use-dockerhub-login-to-avoid-ip-gacha/

またファイル内で使用している環境変数について言及すると「AWS_DEFAULT_REGION」などはデフォルトで使用出来る環境変数なので、CodeBuild等から環境変数を設定する必要がありません(参考:ビルド環境の環境変数)。

一方で、「AWS_ACCOUNT_ID」や「IMAGE_REPO_NAME」は独自で設定しているものなので、buildspec.ymlの中で設定したり、CodeBuildに持たせる必要があります。

最後に出力アーティファクト「imagedefinitions.json」については、CodePipelineのデプロイステージに必要な入力アーティファクトとなります。

printfコマンドにて以下の様な記述を「imagedefinitions.json」に記載します(参考:Amazon ECS 標準デプロイアクション用の imagedefinitions.json ファイル)。

[
  {
    "name": "sample-app",
    "imageUri": "11111EXAMPLE.dkr.ecr.us-west-2.amazonaws.com/ecs-repo:latest"
  }
]

zipファイルの準備

Amazon S3 アクションを使用してパイプラインを接続する前に、Amazon S3 ソースバケットを作成し、ソースファイルを 1 つの ZIP ファイルとしてアップロードしておく必要があります。

Amazon S3 ソースアクションリファレンスより、ソースコードなどの資材をまとめてzip化する必要があります。

貼り付けた画像_2025_01_07_15_34

zip化する前の資材はこの様な感じです。zip化する際の注意は以下の記事に記載しましたのでご確認よろしくお願いします。

https://dev.classmethod.jp/articles/s3-codebuild-yaml_file_error-message-yaml-file-does-not-exist/

CodeBuildの準備

続きましてCodeBuildの作成です。引き続きこちらの記事をベースに作成させていただきました。

ここでは特に注意すべきところを紹介させていただきます。

貼り付けた画像_2025_01_07_15_37

ソースがS3なので、ソースプロバイダを「Amazon S3」に変更します。そのままバケット名やS3オブジェクトキー(先ほど挙げたzipファイル)を指定してください。

貼り付けた画像_2025_01_07_15_41

今回はARMベースでイメージを作成しているので、CodeBuildの実行環境もARMイメージを採用しています。

貼り付けた画像_2025_01_07_15_43

またbuildspec.ymlで使用する環境変数もここで設定します。

CodePipelineの構築

2-3-1. パイプラインの作成にてCodeBuildと同様にCodePipelineの構築を参考にさせていただきました。

貼り付けた画像_2025_01_07_15_52

ソースステージではこの様に設定しています。

検出オプションについては推奨のAmazon CloudWatch Eventsを設定しています。裏側でCloudTrailの証跡やEventBridgeのルールが作成されるので、気になる方は確認してください。

https://dev.classmethod.jp/articles/codepipeline-s3-trigger-hooked-on/

CloudTrailのデータイベントの有効化が必要とのことです、意識し忘れやすい箇所かと思うので覚えておきましょう。

https://blog.denet.co.jp/s3-prefix-object-with-codepipeline/#EventBridgeルール作成

データイベントを有効化すると料金が追加される上、CloudTrailの証跡が2個目になる方も多いと思うので気になる方は上記記事が参考になると思います。

貼り付けた画像_2025_01_07_15_58

ビルドステージはこの様な感じです、特筆すべきことはないです。

貼り付けた画像_2025_01_07_16_01

デプロイプロパイダーに「Amazon ECS(ローリングアップデート)」を選択します。

S3にzipファイルをアップロードして動かす

貼り付けた画像_2025_01_07_17_08

S3に「demo.zip」をアップロードします。

貼り付けた画像_2025_01_07_17_11

CodePipelineが起動するので、成功すればOKです(タスクで使用されているタスク定義が更新されている)。

イメージタグの確認

今回はbuildspec.ymlの中でイメージタグに「CODEBUILD_RESOLVED_SOURCE_VERSION」を使用したので、S3のdemo.zipファイルのバージョンIDがイメージタグに使用されているか確認してみましょう。

貼り付けた画像_2025_01_07_18_30

貼り付けた画像_2025_01_07_18_30

無事に設定されていました!

トラブルシューティング

CICDは構築が完了するまでに何回もエラーが発生するものです、以下は自分が今回の記事を作成するにあたり引っかかった箇所です。

  • S3
    • zip化するディレクトリのミスでbuildspec.ymlが読み込まれなかった(参考)
  • CodeBuild
    • CodeBuildの実行環境のアーキテクチャとFargateのアーキテクチャがARMとx86で異なっており、ECSの起動に失敗
    • CodeBuildの権限不足によるエラー(CloudWatch Logsへの出力やアーティファクトの出力権限不足など)
    • 環境変数の設定ミスによるエラー(値に「/」が含まれていたなど)

この様に特にCodeBuildのエラーが頻発して苦しかったのですが、CodeBuildの実行環境の中にログインしてデバッグすることも可能なので、もし行き詰まった方がいれば以下の記事をご参照することをお勧めします。

https://dev.classmethod.jp/articles/codebuild-supports-accessing-build-environments-with-aws-session-manager/

まとめ

とりあえず簡単なCICDの作成が完了しました。

ブルーグリーンデプロイや、アカウントを跨いだCICDなど検証すべきことはたくさんあるので今後もお楽しみに!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.